package com.zl.oauth.config;

import com.zl.oauth.handler.*;
import com.zl.oauth.intercetor.OAuth2AuthenticationFilter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.*;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.approval.ApprovalStore;
import org.springframework.security.oauth2.provider.approval.TokenApprovalStore;
import org.springframework.security.oauth2.provider.approval.UserApprovalHandler;
import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator;
import org.springframework.security.oauth2.provider.request.DefaultOAuth2RequestFactory;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@Configuration
public class OauthConfig {
    private static final String RESOURCE_ID = "authserver";
    @Bean
    protected UserDetailsService userDetailsService(){
        InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
        manager.createUser(User.withUsername("zhangli").password("123456").authorities("USER").build());
        return manager;
    }
    @Configuration
    @EnableResourceServer
    protected static class ResourceServerConfiguration extends ResourceServerConfigurerAdapter {
        @Autowired
        MyAuthExceptionEntryPoint myAuthExceptionEntryPoint;
        @Autowired
        MyAccessDeniedHandler myAccessDeniedHandler;
        @Override
        public void configure(ResourceServerSecurityConfigurer resources) {
            resources.resourceId(RESOURCE_ID).stateless(false)
            .authenticationEntryPoint(myAuthExceptionEntryPoint)//zhangli2 point 认证失败自定义异常1
            .accessDeniedHandler(myAccessDeniedHandler);//zhangli point3 认证失败自定义异常2
        }

        @Override
        public void configure(HttpSecurity http) throws Exception {
            http.httpBasic()
                    .and().formLogin().loginPage("/authentication/require")//登录请求，需要自定义请求和页面。默认是/login
                    //登录需要经过的url请求
                    .loginProcessingUrl("/authentication/login")
                    .and()
                    .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
                    .and()
                    .authorizeRequests()
//                    .antMatchers("/auth/*").authenticated()
                    .antMatchers("/auth/**").access("#oauth2.hasScope('read') or (!#oauth2.isOAuth() and hasRole('ROLE_USER'))")
                    .antMatchers("/oauth/**").permitAll()
                    .anyRequest().permitAll()//zhangli point1 允许放过哪些请求和拦截哪些请求（或者叫资源）
                    .and()
                    .csrf().disable();//zhangli point5 RESTful技术与CSRF(Cross-site request forgery跨站请求伪造)的冲突，CSRF默认不支持post请求
        }
    }
    @Configuration
    @EnableAuthorizationServer
    protected static class AuthorizationServerConfiguration extends AuthorizationServerConfigurerAdapter {
        @Autowired
        private ClientDetailsService clientDetailsService;//默认有，不需要自己设置
        @Autowired
        TokenStore tokenStore;
        @Autowired
        UserApprovalHandler userApprovalHandler;
        @Autowired
//        @Qualifier("authenticationManagerBean")
        private AuthenticationManager authenticationManager;
        @Autowired
        WebResponseExceptionTranslator oauth2ResponseExceptionTranslator;
//        @Autowired
        private OAuth2AuthenticationFilter OAuth2AuthenticationFilter;
        @Bean
        public TokenStore tokenStore() {
            return new InMemoryTokenStore();
        }
        @Bean
        public ApprovalStore approvalStore() throws Exception {
            TokenApprovalStore store = new TokenApprovalStore();
            store.setTokenStore(tokenStore);
            return store;
        }
        @Bean
        public AuthServerHandler userApprovalHandler() throws Exception {
            AuthServerHandler handler = new AuthServerHandler();
            handler.setApprovalStore(approvalStore());
            handler.setRequestFactory(new DefaultOAuth2RequestFactory(clientDetailsService));
            handler.setClientDetailsService(clientDetailsService);
            handler.setUseApprovalStore(true);
            return handler;
        }


        @Override
        public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
            //内存存储用户信息
            //point3 设置用户信息 clientId secret scope authorities authorizedGrantTypes
            clients.inMemory().withClient("free-web").resourceIds(RESOURCE_ID)
                    .accessTokenValiditySeconds(7200)
                    .authorizedGrantTypes("implicit","client_credentials")//zhangli point4 设置此用户可以使用的授权模式。client_credentials模式请求token方法：body{grant_type:client_credentials} head{Authorization:Basic base64(clientId:sceret)}
                    .authorities("ROLE_CLIENT")
                    .secret("UMHDKEUL")
                    .scopes("read", "write")
                    .redirectUris("")//redirectUris code方式获取token回调路径
            .and().withClient("zhangli").resourceIds(RESOURCE_ID)
                    .accessTokenValiditySeconds(7200)
                    .authorizedGrantTypes("authorization_code")//授权码方式用户
                    .authorities("ROLE_CLIENT")
                    .secret("123456")
                    .scopes("read", "write")
                    .redirectUris("http://localhost:18091/client/fetchCode");
        }
        @Override
        public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
            endpoints.tokenStore(tokenStore).userApprovalHandler(userApprovalHandler)
                    .authenticationManager(authenticationManager).exceptionTranslator(oauth2ResponseExceptionTranslator);
        }

        @Override
        public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
//            oauthServer.addTokenEndpointAuthenticationFilter(OAuth2AuthenticationFilter);
//            super.configure(oauthServer);
            oauthServer.checkTokenAccess("isAuthenticated()");
        }
    }

}
